/* Copyright (C) 2014 InfiniDB, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/* ========================================================================== */
/*                                                                            */
/*   we_indexlist.cpp                                                               */
/*   (c) 2001 Author                                                          */
/*                                                                            */
/*   Description                                                              */
/*                                                                            */
/* ========================================================================== */
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "we_indexlist.h"
using namespace std;

namespace WriteEngine
{
/**
 * Constructor
 */
IndexList::IndexList()
 : m_oid((OID)INVALID_NUM)
 , m_useNarray(true)
 , m_curLevel(INVALID_NUM)
 , m_curBlkPos(0)
 , m_curLevelPos(INVALID_NUM)

{
  m_freemgr.setDebugLevel(DEBUG_0);
  init();
};
/****************************************************************
 * DESCRIPTION:
 * Public Function for adding a header
 * (1) Given a key value and a row ID,
 * (2) Return a pointer for insertion into the correct position
 *     in the Index Tree List Pointer group
 * (3) A return code should indicate success or failur
 * PARAMETERS:
 *    input
 *        pFile      - File Handler
 *        rid        - Input row ID
 *        key        - Input key value
 *    output
 *        listHdrPtr - Output a pointer to the index list header
 * RETURN:
 *    success    - successfully created the index list header
 *    failure    - it did not create the index list header
 *
 *******************************************************************/
const int IndexList::addIndexListHdr(FILE* pFile, const RID& rowId, const uint64_t& key,
                                     IdxEmptyListEntry* newEmptyListPtr)
{
  int rc;
  CommBlock cb;
  m_pFile = pFile;
  cb.file.oid = m_oid;
  cb.file.pFile = m_pFile;
  // Set up the header structure
  // Initialize header blokcs
  rc = resetBlk(&m_hdrBlock);
  m_hdrLbid = INVALID_LBID;
  m_hdrSbid = INVALID_NUM;
  m_hdrEntry = INVALID_NUM;

  // Initialize the new Index List header to null
  memset(&m_curIdxRidListHdr, 0, LIST_HDR_SIZE);
  // Assign the bit fields for the first entry in the Index List Header
  m_curIdxRidListHdr.idxRidListSize.type = LIST_SIZE_TYPE;
  m_curIdxRidListHdr.idxRidListSize.spare = 0x0;
  m_curIdxRidListHdr.idxRidListSize.size = 1;
  // Assign the bit fields for the second entry of the Index List Header
  m_curIdxRidListHdr.key = key;
  // Assign bit fields for the third entry of the Index List Header
  m_curIdxRidListHdr.firstIdxRidListEntry.type = LIST_RID_TYPE;
  m_curIdxRidListHdr.firstIdxRidListEntry.spare = 0x0;
  m_curIdxRidListHdr.firstIdxRidListEntry.rid = rowId;
  // Assign bit fields for the fourth entry of the Index List Header
  m_curIdxRidListHdr.nextIdxRidListPtr.type = LIST_NOT_USED_TYPE;
  m_curIdxRidListHdr.nextIdxRidListPtr.spare = 0x0;
  m_curIdxRidListHdr.nextIdxRidListPtr.llp = 0x0;
  /* Get assigned space for the header from free manager
   * Get the new block for the new idx list header
   * The header needs LIST_HDR_SIZE bytes
   */
  rc = getSegment(pFile, ENTRY_4, newEmptyListPtr);

  if (rc != NO_ERROR)
    return rc;

  m_hdrLbid = newEmptyListPtr->fbo;
  m_hdrSbid = newEmptyListPtr->sbid;
  m_hdrEntry = newEmptyListPtr->entry;

  // Write Index List Header to the file block
  // Write LIST_HDR_SIZE bytes in one time.

  rc = readDBFile(cb, m_hdrBlock.data, m_hdrLbid);
  rc = writeSubBlockEntry(cb, &m_hdrBlock, m_hdrLbid, m_hdrSbid, m_hdrEntry, LIST_HDR_SIZE,
                          &m_curIdxRidListHdr);

  if (rc != NO_ERROR)
  {
    return rc;
  }

  // Wrote Header Block Out already, Start Over next time
  // Update the flags to indicate there is data on the header block
  m_hdrBlock.dirty = true;
  m_hdrBlock.lbid = m_hdrLbid;
  m_hdrBlock.state = BLK_READ;
  m_lastLbid = INVALID_LBID;
  // DONE
  return rc;
};
/****************************************************************
 * DESCRIPTION:
 *
 *
 * RETURN:
 *    success    - successfully created the index list header
 *    failure    - it did not create the index list header
 ***********************************************************/
const int IndexList::updateIndexList(FILE* pFile, const RID& newRid, const uint64_t& key,
                                     IdxEmptyListEntry* curIdxRidListHdrPtr)
{
  int rc;
  m_pFile = pFile;

  // Initialization
  if ((key != m_curIdxRidListHdr.key) || (m_hdrBlock.state == BLK_INIT))
  {
    rc = initGetHdr(key, curIdxRidListHdrPtr);

    if (key != m_curIdxRidListHdr.key)
      return ERR_IDX_LIST_INVALID_KEY;
  }

  rc = updateIndexList(newRid, key);

  if (rc != NO_ERROR)
  {
    return rc;
  }

  // Write everything out
  rc = updateIndexListWrite();
  return rc;
};
/****************************************************************
 * DESCRIPTION:
 * (0) THIS FUNCIION CAN ONLY BE CALLED WITH THE PUBLIC
 *
 * RETURN:
 *    success    - successfully created the index list header
 *    failure    - it did not create the index list header
 ***********************************************************/
const int IndexList::updateIndexList(const RID& newRid, const uint64_t& key)
{
  int rc = NO_ERROR;

  // m_lastLbid==0 or not determines if we can skip from the header,the first
  // subblock or go to the last inserted block
  if (m_lastLbid == (uint64_t)INVALID_LBID)
  {
    rc = updateHdrSub(newRid, key);
  }
  else  // get the lastLbid info from header
  {
    // m_lastLbid > 0, space is in some block now
    m_lbid = m_lastLbid;
    m_sbid = 0;
    m_entry = 0;
    m_segType = LIST_BLOCK_TYPE;
    m_curType = LIST_BLOCK_TYPE;
    rc = addRidInBlk(newRid);
  }

  return rc;
};

/************************************************
 * Description:
 * Find a entry for the given rowId and Key
 * Then Delete it from the list
 * Move the rest of the row id up in the same
 * sub block an decrement the count in that subblock
 * decrement the header size
 * Converted
 * input
 *     pFile       -- File Handler
 *     rowId       -- row id
 *     key         -- value
 *     curIdxRidListHdrPtr - point to the header
 *
 * return value
 *        Success -- 0
 *        Fail    -- ERR_IDX_LIST_INVALID_DELETE
 ************************************************/
const int IndexList::deleteIndexList(FILE* pFile, const RID& rowId, const uint64_t& key,
                                     IdxEmptyListEntry* curIdxRidListHdrPtr)
{
  int rc = ERR_IDX_LIST_INVALID_DELETE;
  m_pFile = pFile;

  getHdrInfo(curIdxRidListHdrPtr);

  if (key != m_curIdxRidListHdr.key)
  {
    memset(m_hdrBlock.data, 0, sizeof(m_hdrBlock.data));
    m_hdrBlock.dirty = false;
    m_hdrBlock.state = BLK_INIT;
    return ERR_IDX_LIST_INVALID_KEY;
  }

  rc = deleteIndexList(rowId, key);
  return rc;
}

/************************************************
 * Description:
 * Converted - keep the first sub block
 * Find a entry for the given rowId and Key
 * Then Delete it from the list
 * Move the rest of the row id up in the same
 * sub block an decrement the count in that subblock
 * decrement the header size
 * input
 *     pFile       -- File Handler
 *     rowId       -- row id
 *     key         -- value
 *     curIdxRidListHdrPtr - point to the header
 *
 * return value
 *        Success -- 0
 *        Fail    -- ERR_IDX_LIST_INVALID_DELETE
 ************************************************/
const int IndexList::deleteIndexList(const RID& rowId, const uint64_t& key)
{
  int rc = ERR_IDX_LIST_INVALID_DELETE;
  RID savedRid;
  DataBlock prevDataBlock;

  CommBlock cb;
  cb.file.oid = m_oid;
  cb.file.pFile = m_pFile;

  // Check the first row location, 3rd entry
  // Because it may be deleted from the delete action
  // The header size cannot tell us the rowid size on header
  if (m_curIdxRidListHdr.firstIdxRidListEntry.type == (int)LIST_RID_TYPE)
  {
    if (m_curIdxRidListHdr.firstIdxRidListEntry.rid == rowId)
    {
      m_curIdxRidListHdr.firstIdxRidListEntry.type = LIST_NOT_USED_TYPE;  // not used type
      m_curIdxRidListHdr.firstIdxRidListEntry.rid = 0;
      m_curIdxRidListHdr.idxRidListSize.size--;
      rc = writeSubBlockEntry(cb, &m_hdrBlock, m_hdrLbid, m_hdrSbid, m_hdrEntry, LIST_HDR_SIZE,
                              &m_curIdxRidListHdr);
      memset(m_hdrBlock.data, 0, sizeof(m_hdrBlock.data));
      m_hdrBlock.dirty = false;
      m_dLbid = m_hdrLbid;
      m_dSbid = m_hdrSbid;
      m_dEntry = m_hdrEntry + 2;

      return rc;
    }
  };

  // Check Header last entry's type
  int type = m_curIdxRidListHdr.nextIdxRidListPtr.type;

  switch (type)
  {
    case LIST_NOT_USED_TYPE:  // Header is not full, no sub-block linked
      // No RowId here
      memset(m_hdrBlock.data, 0, sizeof(m_hdrBlock.data));
      m_hdrBlock.dirty = false;
      m_dLbid = -1LL;
      m_dSbid = -1;
      m_dEntry = -1;
      return ERR_IDX_LIST_INVALID_DELETE;  // not found, failed

    case LIST_RID_TYPE:  // There is a row id here, Check!
      savedRid = m_curIdxRidListHdr.nextIdxRidListPtr.llp;

      if (savedRid == rowId)
      {
        m_curIdxRidListHdr.nextIdxRidListPtr.type = LIST_NOT_USED_TYPE;
        m_curIdxRidListHdr.nextIdxRidListPtr.llp = 0;
        m_curIdxRidListHdr.idxRidListSize.size--;
        rc = writeSubBlockEntry(cb, &m_hdrBlock, m_hdrLbid, m_hdrSbid, m_hdrEntry, LIST_HDR_SIZE,
                                &m_curIdxRidListHdr);
        m_hdrBlock.dirty = false;
        memset(m_hdrBlock.data, 0, sizeof(m_hdrBlock.data));
        m_dLbid = m_hdrLbid;
        m_dSbid = m_hdrSbid;
        m_dEntry = 3;

        return rc;
      }
      else
      {
        m_hdrBlock.dirty = false;
        memset(m_hdrBlock.data, 0, sizeof(m_hdrBlock.data));
        m_dLbid = -1LL;
        m_dSbid = -1;
        m_dEntry = -1;
        return ERR_IDX_LIST_INVALID_DELETE;
      }

    case LIST_SUBBLOCK_TYPE:  // Not found in header,
      rc = deleteInSub(rowId);

      if (rc == NO_ERROR)
        return rc;

      rc = deleteInBlock(rowId);
      return rc;
      break;

    default: break;
  };  // end of switch

  return ERR_IDX_LIST_INVALID_DELETE;
}
}  // namespace WriteEngine
